<template>
  <el-dialog :title="dialogTitle[fileType as 0 | 1 | 2]" :model-value="showImageDialogVisible" width="760"
    @close="closeDialog" :close-on-click-modal="false">
    <el-form :model="formData">
      <el-form-item label="上传到：">
        <el-select v-model="formData.groupId" style="width: 300px">
          <el-option v-for="item in groupList" :key="item.id" :label="item.groupName" :value="item.id" />
        </el-select>
      </el-form-item>
    </el-form>

    <div v-loading="uploadLoading">
      <el-upload class="avatar-uploader" :accept="acceptType[fileType as 0 | 1 | 2]" :action="server"
        :headers="imgHeaders" :show-file-list="false" drag multiple :on-success="handleSuccess"
        :before-upload="beforeUpload" :on-change="handleChange">
        <div class="upload-tip">
          <i class="el-icon-plus avatar-uploader-icon"><el-icon>
              <Plus />
            </el-icon></i>
          <div>拖入文件或<em>点击上传</em></div>
        </div>
      </el-upload>
    </div>

    <template #footer>
      <div class="dialog-footer">
        <el-button @click="closeDialog">关闭</el-button>
        <el-button type="primary" :loading="submitLoading" @click="submit">提交</el-button>
      </div>
    </template>
  </el-dialog>
</template>

<script setup lang="ts">
import { Plus } from '@element-plus/icons-vue'
import { useUserStore } from '@/store/modules/user'
import { ElMessage } from 'element-plus'
import { getMaterialGroup, addListFile } from '@/api/materialManagement'
const userStore = useUserStore()
let server = `${import.meta.env.VITE_API_URL}files`
let { accessToken } = userStore
const imgHeaders = { Authorization: 'Bearer ' + accessToken }

const props = defineProps({
  showImageDialogVisible: {
    type: Boolean
  },
  activeGroup: {
    type: Object
  },
  fileType: {
    type: Number
  }
})

const dialogTitle: Record<0 | 1 | 2, string> = {
  0: '上传图片',
  1: '上传视频',
  2: '上传文档'
}

const acceptType: Record<0 | 1 | 2, string> = {
  0: 'image/*',
  1: 'video/*',
  2:
    '.doc,.docx,.xls,.xlsx,.ppt,.pptx,.pdf,' +
    'application/msword,' +
    'application/vnd.openxmlformats-officedocument.wordprocessingml.document,' +
    'application/vnd.ms-excel,' +
    'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,' +
    'application/vnd.ms-powerpoint,' +
    'application/vnd.openxmlformats-officedocument.presentationml.presentation,' +
    'application/pdf'
}

const emit = defineEmits(['closeDialog'])

interface UploadImageInfo {
  fileName: string
  fileUrl: string
  fileExtension: string
  fileSize: number
  filePixels: string
}

interface GroupItem {
  id: string
  groupName: string
}

interface UploadForm {
  groupId: string | number
  imgList: UploadImageInfo[]
}

const formData = reactive<UploadForm>({
  groupId: '',
  imgList: []
})

const groupList = ref<GroupItem[]>([])

const uploadFileMap = new Map<string, { width: number; height: number }>()

const submitLoading = ref(false)
const uploadLoading = ref(false)
const completedUploadCount = ref(0)
const passedFiles = ref(0)

const handleSuccess = (response: any, uploadFile: any) => {
  const uid = uploadFile.uid
  const fileName = uploadFile.name
  const fileSize = uploadFile.size
  const fileExt = '.' + fileName.split('.').pop()
  const fileUrl = response?.data?.url || ''

  const type = props.fileType as 0 | 1 | 2
  const sizeInfo = uploadFileMap.get(uid)

  if (response.code === '00000' && fileUrl) {
    let filePixels = ''

    if (type === 0 && sizeInfo) {
      // 图片：宽×高
      filePixels = `${sizeInfo.width}×${sizeInfo.height}`
    } else if (type === 1 && sizeInfo) {
      // 视频：duration 秒
      filePixels = `${Math.round(sizeInfo.width)}`
    }

    formData.imgList.push({
      fileName,
      fileUrl,
      fileExtension: fileExt,
      fileSize,
      filePixels
    })
  } else {
    ElMessage.error(`上传失败：${fileName}`)
  }

  completedUploadCount.value++

  if (completedUploadCount.value === passedFiles.value) {
    uploadLoading.value = false
    setTimeout(() => {
      submit()
    }, 300)
  }
}

const beforeUpload = async (file: any) => {
  const type = props.fileType as 0 | 1 | 2

  if (type === 0) {
    // 图片校验
    const isImage = ['image/jpeg', 'image/png', 'image/jpg', 'image/gif'].includes(file.type)
    const isLt20M = file.size / 1024 / 1024 < 20
    if (!isImage) {
      ElMessage.error('图片类型仅支持 JPG / PNG / GIF 格式!')
      return false
    }
    if (!isLt20M) {
      ElMessage.error('图片大小不能超过 20MB!')
      return false
    }
    try {
      const { width, height } = await getImgSize(file)
      uploadFileMap.set(file.uid, { width, height })
      passedFiles.value++
      return true
    } catch {
      ElMessage.error('读取图片尺寸失败')
      return false
    }
  }

  if (type === 1) {
    // 视频校验
    const isVideo = file.type === 'video/mp4'
    const isLt100M = file.size / 1024 / 1024 < 100
    if (!isVideo) {
      ElMessage.error('视频格式仅支持 MP4')
      return false
    }
    if (!isLt100M) {
      ElMessage.error('视频大小不能超过 100MB!')
      return false
    }
    try {
      const duration = await getVideoDuration(file)
      uploadFileMap.set(file.uid, { width: duration, height: 0 })
      passedFiles.value++
      return true
    } catch {
      ElMessage.error('读取视频时长失败')
      return false
    }
  }

  if (type === 2) {
    // 文档校验
    const allowedTypes = [
      'application/msword',
      'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
      'application/vnd.ms-excel',
      'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
      'application/vnd.ms-powerpoint',
      'application/vnd.openxmlformats-officedocument.presentationml.presentation',
      'application/pdf'
    ]
    const isDoc = allowedTypes.includes(file.type)
    const isLt10M = file.size / 1024 / 1024 < 10
    if (!isDoc) {
      ElMessage.error('文档类型仅支持 Word / Excel / PPT / PDF 文件')
      return false
    }
    if (!isLt10M) {
      ElMessage.error('文档大小不能超过 10MB!')
      return false
    }
    passedFiles.value++
    return true
  }

  ElMessage.error('不支持的文件类型')
  return false
}

function handleChange(file: any, fileList: any) {
  uploadLoading.value = true
}

function getImgSize(file: File): Promise<{ width: number; height: number }> {
  return new Promise((resolve, reject) => {
    const reader = new FileReader()
    reader.onload = (e: any) => {
      const img = new Image()
      img.src = e.target.result
      img.onload = () => {
        resolve({ width: img.width, height: img.height })
      }
      img.onerror = () => reject()
    }
    reader.readAsDataURL(file)
  })
}

function getVideoDuration(file: File): Promise<number> {
  return new Promise((resolve, reject) => {
    const video = document.createElement('video')
    video.preload = 'metadata'
    video.onloadedmetadata = () => {
      URL.revokeObjectURL(video.src)
      resolve(video.duration)
    }
    video.onerror = () => reject()
    video.src = URL.createObjectURL(file)
  })
}

function closeDialog() {
  emit('closeDialog', false)
}

async function getData() {
  const res = await getMaterialGroup({
    fileType: props.fileType
  })
  if (res.code === '00000') {
    groupList.value = res.data
    if (props.activeGroup?.id) {
      formData.groupId = props.activeGroup.id
    } else {
      const defaultGroup = res.data.find((item: GroupItem) => item.groupName === '默认分组')
      formData.groupId = defaultGroup ? defaultGroup.id : ''
    }
  }
}

async function submit() {
  try {
    submitLoading.value = true
    const res = await addListFile({
      fileType: props.fileType,
      groupId: formData.groupId,
      fileVos: formData.imgList
    })
    if (res.code === '00000') {
      ElMessage.success('添加成功')
      emit('closeDialog', true)
    }
  } catch (e) {
  } finally {
    submitLoading.value = false
  }
}

onMounted(() => {
  getData()
})
</script>

<style scoped lang="scss">
.avatar-uploader {
  width: 100%;
  min-height: 200px;
  border-radius: 6px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  text-align: center;

  .upload-tip {
    width: 100%;
    display: flex;
    flex-direction: column;
  }

  .avatar-uploader-icon {
    font-size: 28px;
    color: #8c939d;
    width: 100%;
    height: 100px;
    line-height: 100px;
  }

  .avatar {
    width: 100%;
    height: 100px;
    display: block;
  }
}
</style>
